<!--
 * @Author: 九璃怀特 1599130621@qq.com
 * @Date: 2022-10-11 15:26:33
 * @LastEditors: 九璃怀特 1599130621@qq.com
 * @LastEditTime: 2022-10-26 14:19:35
 * @FilePath: \nonogram\src\components\nonogram\nonogram.vue
 * @Description: 
 -->
<template>
  <div
    class="nonoGram"
    :style="{
      width: `${gramWidth}px`,
      height: `${gramWidth}px`,
      minWidth: `${gramWidth}px`,
      'grid-template-columns': `${20}% ${60}% ${20}%`,
      'grid-template-rows': `${20}% ${60}% ${20}%`,
    }"
  >
    <div
      class="gramView"
      ref="gramView"
      ignore
      v-if="props.select"
      :style="{
        gridTemplateColumns: `repeat(${grids}, minmax(0, 1fr))`,
        gridTemplateRows: `repeat(${grids}, minmax(0, 1fr))`,
      }"
    >
      <template v-for="(rows, r) in viewGrid" :key="r">
        <template v-for="(item, c) in rows" :key="c">
          <div class="lit-gram-item">
            <div
              :style="{
                backgroundColor: item
                  ? item == 1
                    ? props.colors[0]
                    : props.colors[1]
                  : 'white',
              }"
            />
          </div>
        </template>
      </template>
    </div>

    <div class="gramTop" :style="{ height: `100%`, fontSize: `${font}px` }">
      <template v-for="(rows, r) in arrC" :key="r">
        <div class="colList">
          <template v-for="(item, c) in maxsum">
            <div
              class="textdom"
              :style="{
                transform: `scale(${IsPhone ? 1 : font < 10 ? font * 0.1 : 1})`,
              }"
            >
              {{ rows[c] }}
            </div>
          </template>
        </div>
      </template>
    </div>
    <div
      class="gramLeft"
      :style="{ width: `100%`, height: `100%`, fontSize: `${font}px` }"
    >
      <template v-for="(rows, r) in arrR" :key="r">
        <div class="rowList">
          <template v-for="(item, c) in maxsum">
            <div
              class="textdom"
              :style="{
                transform: `scale(${IsPhone ? 1 : font < 10 ? font * 0.1 : 1})`,
              }"
            >
              {{ rows[c] }}
            </div>
          </template>
        </div>
      </template>
    </div>
    <div class="gramCore" :style="containerStyle">
      <template v-for="(rows, r) in lines - 1" :key="r">
        <div
          class="rowLines"
          :style="{ top: `${((gramWidth * 0.6 - 4) / lines) * (r + 1)}px` }"
        ></div>
        <div
          class="colLines"
          :style="{ left: `${((gramWidth * 0.6 - 4) / lines) * (r + 1)}px` }"
        ></div>
      </template>
      <template v-for="(rows, r) in viewGrid" :key="r">
        <template v-for="(item, c) in rows" :key="c">
          <div
            class="gram-item"
            @contextmenu.prevent="setColor(r, c, 2)"
            @click="setColor(r, c, 1)"
            :style="{ padding: `${itemPidding < 1 ? 1 : itemPidding}px` }"
          >
            <div
              :style="{
                backgroundColor: item
                  ? item == 1
                    ? props.colors[0]
                    : props.colors[1]
                  : 'white',
              }"
            />
          </div>
        </template>
      </template>
    </div>
    <div class="gramRight" ignore>
      <slot name="Right"></slot>
    </div>
    <div class="gramBottom" ignore>
      <slot name="Bottom"></slot>
    </div>
    <div class="gramRight-Top" ignore>
      <slot name="Right-Top"></slot>
    </div>
    <div class="gramLeft-Bottom" ignore>
      <slot name="Left-Bottom"></slot>
    </div>
  </div>
</template>
<script setup lang="ts">
import {
  computed,
  onBeforeMount,
  onMounted,
  PropType,
  ref,
  toRaw,
  watch,
} from "vue";
import { Textile5x5, Textile10x10, Textile15x15 } from "./Textile";
import domtoimage from "dom-to-image-more";
const props = defineProps({
  select: { type: Boolean, default: true },
  width: { type: Number, default: 400 },
  level: { type: Number, default: 1 },
  colors: {
    type: Array as PropType<Array<string>>,
    default: ["#000000", "#008080"],
  },
  create: {
    type: Boolean,
    default: false,
  },
});
const IsPhone = /mobile/i.test(navigator.userAgent);
const level = ref(props.level);
// 网格
const grids = computed(() => {
  return level.value * 5;
});
// dom宽度
const gramWidth = ref(props.width);
//
const lines = computed(() => {
  return grids.value / 5;
});
const maxsum = computed(() => {
  return Math.ceil(grids.value / 2);
});
// 字体大小
const font = computed(() => {
  const domMax = Math.floor((props.width * 0.2) / maxsum.value);
  const size = Math.floor(((props.width * 0.6) / grids.value - 6) * 0.7);
  return domMax > size ? size : domMax;
});

const containerStyle = computed(() => ({
  gridTemplateColumns: `repeat(${grids.value}, minmax(0, 1fr))`,
  gridTemplateRows: `repeat(${grids.value}, minmax(0, 1fr))`,
  // width: `${gramWidth}px`,
  // height: `${gramWidth}px`,
  cursor: `${props.select ? "pointer" : "default"}`,
}));

// 保存自定义
const textiles: { [key: string]: number[][][] } = {
  t5: [...Textile5x5],
  t10: [...Textile10x10],
  t15: [...Textile15x15],
};
for (const key in textiles) {
  const v: number[][][] = JSON.parse(localStorage.getItem(key) || "null");
  if (v) {
    v.forEach((e) => {
      textiles[key].push(e);
    });
  }
}

// 初始化绘制网格
const gameGrid = ref<number[][]>(initGrid());
// 初始化目标网格
const targetGrid = ref<number[][]>(initGrid(true));
// 当前展示的网格
const viewGrid = computed(() => {
  return props.select && !props.create ? gameGrid.value : targetGrid.value;
});

// 数字 行
const arrR = ref<number[][]>([]);
// 数字 列
const arrC = ref<number[][]>([]);
// 根据网格生成对应解题数字
function setNum() {
  arrR.value.length = arrC.value.length = grids.value;
  const arr = targetGrid.value;
  for (let i = 0; i < grids.value; i++) {
    let sumC = 0,
      sumR = 0;
    arrR.value[i] = [];
    arrC.value[i] = [];
    for (let j = 0; j < grids.value; j++) {
      if (arr[j][i] == 1) {
        sumC++;
        if (j == grids.value - 1) {
          arrC.value[i].unshift(sumC);
        }
      } else {
        if (sumC != 0) {
          arrC.value[i].unshift(sumC);
        }
        sumC = 0;
      }
      if (arr[i][j] == 1) {
        sumR++;
        if (j == grids.value - 1) {
          arrR.value[i].unshift(sumR);
        }
      } else {
        if (sumR != 0) {
          arrR.value[i].unshift(sumR);
        }
        sumR = 0;
      }
    }
  }
}
// 校验
function check() {
  let pending = true;
  // 错误
  let errs = 0;
  // 未完成
  let lacks = 0;
  for (let i = 0; i < grids.value; i++) {
    for (let j = 0; j < grids.value; j++) {
      const t = targetGrid.value[i][j],
        v = gameGrid.value[i][j];
      if (t == 1) {
        if (v != t) {
          pending = false;
          lacks++;
        }
      } else {
        if (v == 1) errs++;
      }
    }
  }
  return { pending, lacks, errs };
}
// 初始化
function initGrid(random = false) {
  const r = textiles[`t${grids.value}`];
  const i = r ? Math.round(Math.random() * (r?.length - 1)) : 0;
  const TextArr = r ? copyGrid(r[i]) : null;
  const randomText = Array(grids.value)
    .fill(null)
    .map((e) =>
      Array(grids.value)
        .fill(null)
        .map((g) => (random ? Number(Math.random() > 0.5) : 0))
    );
  return random
    ? Math.random() > 0.5 && TextArr
      ? TextArr
      : randomText
    : randomText;
}

// 重置
function reset() {
  viewGrid.value.forEach((e, i) => {
    e.forEach((n, j) => {
      viewGrid.value[i][j] = 0;
    });
  });
}
// 重新生成
function replace() {
  if (!props.create) {
    if (Math.random() > 0.5 && grids.value == 10) {
      targetGrid.value = Textile10x10[0];
    } else if (Math.random() > 0.5 && grids.value == 15) {
      targetGrid.value = Textile15x15[0];
    } else {
      targetGrid.value = initGrid(true);
    }
  } else {
    targetGrid.value = initGrid();
    gameGrid.value = initGrid(true);
  }

  reset();
}
// 目标数组改变时 更新数字
watch(targetGrid, setNum, { deep: true });
// 更改level
watch(
  () => props.level,
  (v) => {
    level.value = v;
    itemPidding.value = Math.round(((props.width * 0.6) / grids.value - 4) / 28)

    if (!props.create) {
      targetGrid.value = initGrid(true);
      gameGrid.value = initGrid();
    } else {
      targetGrid.value = initGrid();
      gameGrid.value = initGrid(true);
    }
  }
);
// 点击后设置颜色
function setColor(r: number, c: number, value: number) {
  if (!props.select) return;
  if (viewGrid.value[r][c] == value) {
    viewGrid.value[r][c] = 0;
  } else {
    viewGrid.value[r][c] = value;
  }
}
const gramView = ref<HTMLDivElement>();
function getCanvas() {
  const img = new Image();
  domtoimage.toPng(gramView.value!).then((url) => {
    img.src = url;
  });
  return img;
}
watch(
  () => props.create,
  (v) => {
    if (v) {
      gameGrid.value = copyGrid(targetGrid.value);
      targetGrid.value = initGrid();
    }
  }
);
// 确认提交 保存
function setTextileStore() {
  const t = grids.value;
  textiles[`t${t}`].push(toRaw(targetGrid.value));
  localStorage.setItem(`t${t}`, JSON.stringify(textiles[`t${t}`]));
  gameGrid.value = initGrid();
}
// 取消提交 恢复
function resetTargetGrid() {
  targetGrid.value = copyGrid(gameGrid.value);
  gameGrid.value = initGrid();
}
// 复制一个数织
function copyGrid(arr2: Array<Array<number>>) {
  const arr1: Array<Array<number>> = Array(grids.value)
    .fill(null)
    .map((e) =>
      Array(grids.value)
        .fill(null)
        .map((g) => 0)
    );
  arr1.forEach((e, i) => {
    e.forEach((m, index) => {
      arr1[i][index] = arr2[i][index];
    });
  });
  return arr1;
}
onBeforeMount(setNum);
const itemPidding = ref(
  Math.round(((props.width * 0.6) / grids.value - 4) / 28)
);

onMounted(() => {});
defineExpose({
  check,
  reset,
  replace,
  getCanvas,
  targetGrid,
  resetTargetGrid,
  setTextileStore,
});
</script>

<style scoped lang="less">
.nonoGram {
  display: grid;
  align-content: center;
  justify-content: center;
  align-content: stretch;
  border-radius: 5px;
  user-select: none;

  .textdom {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    // transform: scale(.8);
  }

  .gramView {
    margin: 0 20% 20% 0;
    grid-column-start: 1;
    grid-row-start: 1;
    display: grid;
    border: 2px solid black;
    position: relative;
    border-radius: 3px;
    pointer-events: none;
    .lit-gram-item {
      background-color: rgb(255, 255, 255);
      display: inline-grid;
      pointer-events: none;
    }
  }

  .gramTop {
    display: flex;
    grid-column-start: 2;
    grid-row-start: 1;
    flex-direction: row;
    pointer-events: none;

    .colList {
      flex: 1;
      display: flex;
      flex-direction: column-reverse;
    }
  }

  .gramBottom {
    display: flex;
    grid-column-start: 2;
    grid-row-start: 3;
    justify-content: space-between;
  }

  .gramLeft {
    display: flex;
    flex-direction: column;
    pointer-events: none;
    grid-column-start: 1;
    grid-row-start: 2;

    .rowList {
      flex: 1;
      display: flex;
      justify-content: flex-end;
      align-items: center;
      flex-direction: row-reverse;
    }
  }
  .gramRight {
    grid-column-start: 3;
    grid-row-start: 2;
  }
  .gramRight-Top {
    grid-column-start: 3;
    grid-row-start: 1;
  }
  .gramLeft-Bottom {
    grid-column-start: 1;
    grid-row-start: 3;
  }

  .gramCore {
    display: grid;
    border: 2px solid black;
    position: relative;
    border-radius: 3px;
    grid-column-start: 2;
    grid-row-start: 2;

    .rowLines {
      position: absolute;
      width: 100%;
      transform: translateY(-50%);
      height: 2px;
      background-color: Crimson;
    }

    .colLines {
      position: absolute;
      height: 100%;
      transform: translateX(-50%);
      width: 2px;
      background-color: Crimson;
    }

    .gram-item {
      background-color: rgb(255, 255, 255);
      padding: 1px;
      display: inline-grid;
      border: 1px solid black;
    }
  }
}
</style>
